package cn.kai.process.flow.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.kai.enums.FlowContextEnums;
import cn.kai.enums.FlowNodeTypeEnums;
import cn.kai.json.flow.FlowChainJson;
import cn.kai.json.flow.FlowJson;
import cn.kai.json.flow.FlowNodeJson;
import cn.kai.process.cache.Cache;
import cn.kai.process.chain.CFRunChain;
import cn.kai.process.flow.node.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * 抽象流程引擎父类
 Author: cKnight
 Date: 2025/2/7
 */
@Slf4j
public abstract class AbstractFlowEngine {
    /** 缓存链 */
    private final Cache<String, FlowChain> chains;
    /** 线程池 */
    private final Executor executor;

    public AbstractFlowEngine(Cache<String, FlowChain> chains, Executor executor) {
        this.chains = chains;
        this.executor = executor;
    }

    /** 注册一个流程链 */
    public void registerChain(String name, FlowChain chain) {
        chains.put(name, chain);
    }

    /** 执行一个流程链 */
    public void execute(String name) {
        execute(name, new NodeContext());
    }

    /** 执行一个流程链 */
    public void execute(String name, NodeContext nodeContext) {
        FlowChain flowChain = chains.get(name);
        if (Objects.isNull(flowChain)) {
            throw new IllegalArgumentException("流程链未找到:" + name);
        }
        flowChain.execute(nodeContext);
    }

    /** 执行一个有返回值的流程链 */
    public <T> T execute(String name,String resultKey, T defaultValue){
        NodeContext nodeContext = new NodeContext();
        FlowChain flowChain = chains.get(name);
        if (Objects.isNull(flowChain)) {
            throw new IllegalArgumentException("流程链未找到:" + name);
        }
        flowChain.execute(nodeContext);
        T data = (T) nodeContext.getData(resultKey);
        return Objects.nonNull(data) ? data : defaultValue;
    }


    public <T> T execute(String name, T defaultValue){
        return execute(name, FlowContextEnums.RESULT.name(),defaultValue);
    }

    /** 注册并启动 */
    public void execute(FlowChainJson chainJson) {
        FlowChain flowChain = parseFlowChain(chainJson);
        registerChain(flowChain.getName(), flowChain);
        execute(flowChain.getName());
    }

    /** 更新一个流程链 */
    public void updateChain(String name, FlowChain chain) {
        chains.put(name, chain);
    }

    /** 获取所有的流程链 */
    public Map<String, FlowChain> getAllChain() {
        return chains.getCache();
    }

    /** 链是否存在 */
    public boolean isExist(String name){
        return chains.get(name) != null;
    }

    /** 解析并注册 */
    public void registerChain(FlowJson flow) {
        for (FlowChainJson chain : flow.getChains()) {
            if (!chain.getIsLazy()) {
                FlowChain flowChain = parseFlowChain(chain);
                if (Objects.nonNull(flowChain)) {
                    registerChain(flowChain.getName(), flowChain);
                }
            }
        }
    }

    /** 解析单个 */
    public void registerChain(FlowChainJson chainJson) {
        FlowChain flowChain = parseFlowChain(chainJson);
        registerChain(flowChain.getName(), flowChain);
    }

    /** 解析一个chain */
    public FlowChain parseFlowChain(FlowChainJson chainJson) {
        String name = chainJson.getName();
        if (StringUtils.isBlank(name)) {
            log.warn("解析chain失败，未发现chain名称");
            return null;
        }
        FlowChain flowChain = new FlowChain(name);
        List<Node> nodes = parseNodeList(chainJson.getNodes());
        flowChain.setNodes(nodes);
        return flowChain;
    }

    private List<Node> parseNodeList(List<FlowNodeJson> nodes) {
        List<Node> list = new ArrayList<>();
        for (FlowNodeJson nodeJson : nodes) {
            Node node = parseNode(nodeJson);
            if (Objects.nonNull(node)) {
                list.add(node);
            } else {
                log.warn("节点解析失败:{}", nodeJson);
            }
        }
        return list;
    }

    private List<Node> parseNodeList(List<FlowNodeJson> nodes, List<Node> list) {
        for (FlowNodeJson nodeJson : nodes) {
            Node node = parseNode(nodeJson);
            if (Objects.nonNull(node)) {
                list.add(node);
            } else {
                log.warn("节点解析失败:{}", nodeJson);
            }
        }
        return list;
    }

    private Node parseNode(FlowNodeJson node) {
        FlowNodeTypeEnums type = node.getType();
        if (Objects.isNull(type)) {
            throw new IllegalArgumentException("节点类型为空:" + node);
        }
        String component = node.getComponent();
        Node bean = null;
        if (StringUtils.isNotBlank(component)) {
            bean = SpringUtil.getBean(component, Node.class);
        }
        switch (type) {
            case NODE:
                checkNode(component, bean);
                return bean;
            case IF:
                String condition = node.getCondition();
                List<FlowNodeJson> trueChain = node.getTrueChain();
                List<FlowNodeJson> falseChain = node.getFalseChain();
                if (StringUtils.isBlank(condition)) {
                    throw new IllegalArgumentException("未获取到节点条件key:" + node);
                }
                if (CollUtil.isEmpty(trueChain) || CollUtil.isEmpty(falseChain)) {
                    throw new IllegalArgumentException("未获取到节点条件true或false链:" + node);
                }
                //并行解析双分支
                List<Node> trueNodes = new ArrayList<>();
                List<Node> falseNodes = new ArrayList<>();
                CFRunChain.start(executor)
                        .next(() -> parseNodeList(trueChain, trueNodes))
                        .next(() -> parseNodeList(falseChain, falseNodes))
                        .end(60L);
                return new ConditionNode(condition, new ChainNode(trueNodes), new ChainNode(falseNodes));
            case LOOP:
                checkNode(component, bean);
                return new LoopNode(bean, node.getTimes());
            case ASYNC:
                checkNode(component, bean);
                return new AsyncNode(bean);
            case RETRY:
                checkNode(component, bean);
                return new RetryNode(bean, node.getTimes());
            case PARALLEL:
                List<FlowNodeJson> parallelList = node.getTrueChain();
                if (CollUtil.isEmpty(parallelList)) {
                    throw new IllegalArgumentException("并行节点获取到nodes:" + node);
                }
                List<Node> nodes = parseNodeList(parallelList, new ArrayList<>());
                return new ParallelNode(nodes, node.getTimes());
            default:
                throw new IllegalArgumentException("node类型错误:" + node);
        }
    }

    private void checkNode(String component, Node node) {
        if (StringUtils.isBlank(component) || Objects.isNull(node)) {
            throw new IllegalArgumentException("未获取到节点组件");
        }
    }
}
